package br.org.acessobrasil.processoacessibilidade.bo;

import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import br.org.acessobrasil.ases.ferramentas_de_reparo.controle.XHTMLControle;
import br.org.acessobrasil.ases.nucleo.InterfClienteDoNucleo;
import br.org.acessobrasil.ases.nucleo.NucleoEstruturado;
import br.org.acessobrasil.ases.nucleo.adapters.entidade.ArmazenaErroOuAviso;
import br.org.acessobrasil.ases.regras.RegrasHardCodedEmag;
import br.org.acessobrasil.processoacessibilidade.vo.ErroOuAvisoPro;
import br.org.acessobrasil.processoacessibilidade.vo.ErrosRegraPro;
import br.org.acessobrasil.processoacessibilidade.vo.RelatorioAcessibilidadePro;

public class Avaliador implements InterfClienteDoNucleo {
	private int correcaoLinha = 0;
	private NucleoEstruturado nucleo;
	private RegrasHardCodedEmag regras = new RegrasHardCodedEmag();
	private boolean errado=false;
	private RelatorioAcessibilidadePro relatorio;
	private ArrayList<ErrosRegraPro> listErroP1 = new ArrayList<ErrosRegraPro>();
	private ArrayList<ErrosRegraPro> listErroP2 = new ArrayList<ErrosRegraPro>();
	private ArrayList<ErrosRegraPro> listErroP3 = new ArrayList<ErrosRegraPro>();
	private ArrayList<ErrosRegraPro> listErroXHTML = new ArrayList<ErrosRegraPro>();
	private ArrayList<ErrosRegraPro> listAvisoP1 = new ArrayList<ErrosRegraPro>();
	private ArrayList<ErrosRegraPro> listAvisoP2 = new ArrayList<ErrosRegraPro>();
	private ArrayList<ErrosRegraPro> listAvisoP3 = new ArrayList<ErrosRegraPro>();
	
	private List<ArmazenaErroOuAviso> avisoP1;
	private List<ArmazenaErroOuAviso> avisoP2;
	private List<ArmazenaErroOuAviso> avisoP3;
	private List<ArmazenaErroOuAviso> avisoXHTML=new ArrayList<ArmazenaErroOuAviso>();
	private List<ArmazenaErroOuAviso> erroP1;
	private List<ArmazenaErroOuAviso> erroP2;
	private List<ArmazenaErroOuAviso> erroP3;
	private List<ArmazenaErroOuAviso> erroXHTML=new ArrayList<ArmazenaErroOuAviso>();
	private static String dtdPath;
	private static boolean dtdPathVerificado=false;
	static {
		//Configura o idioma do ases
		br.org.acessobrasil.silvinha.util.lang.TokenLang.LANG="pt";
	}

	
	
	public Avaliador(){
		nucleo = new NucleoEstruturado();
		nucleo.addCliente(this);	
	}
	
	/**
	 * Verifica o path do DTD uma vez
	 */
	private static void checkDtdPath(){
		if(dtdPathVerificado) return;
		dtdPathVerificado = true;
		if(!(dtdPath.equals("default") || dtdPath.equals(""))){
			//caso n�o seja o default verificar se existe o DTD
			try{
				URL dtdUrl = new URL(dtdPath+"/xhtml1-strict.dtd");
				dtdUrl.openConnection().getContent();
			}catch(Exception e){
				e.printStackTrace();
			}
		}
	}
	/**
	 * Configura o caminho do path
	 * Ex.: http://www.seusite.com.br/dtd
	 * @param path
	 */
	public void setDtdBasePath(String path){
		if(path.equals("default") || path.equals("")){
			XHTMLControle.XHTML_01Strict="<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">";
		}else{
			//Configura o local dos DTDs
			XHTMLControle.XHTML_01Strict="<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \""+path+"/xhtml1-strict.dtd\">";
			XHTMLControle.HTML4_01Strict="<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Strict//EN\" \""+path+"/html4.01strict.dtd\">";
			XHTMLControle.HTML4_01Transitional="<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \""+path+"/html4.01transitional.dtd\">";
			XHTMLControle.HTML4_01Frameset="<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML Frameset//EN\" \""+path+"/html4.01frameset.dtd\">";
		}
		dtdPath = path;
	}
	
	
	/**
	 * Fecha as tags (br,img)
	 * Coloca as tags em min�sculas
	 * @param conteudoFormatado
	 */
	public String tratar(String conteudo){
		return conteudo
			.replaceAll("<img\\s([^>]*[^/])>","<img $1 />")
			.replace("<br>", "<br />");		
	}
	/**
	 * Avalia o padr�o html em lowerCase <br>
	 * Avalia a Acessibilidade com casesensitive <br>
	 * 
	 * @param html c�digo html
	 */
	public void avaliarHtml(String html){
		checkDtdPath();
		nucleo.setRegras(regras);
		nucleo.setCodHTML(html);
		nucleo.setAvaliaCSS(false);
		try {
			nucleo.setWCAGEMAG(NucleoEstruturado.EMAG);
		} catch (RuntimeException e) {
			e.printStackTrace();
		}
		nucleo.avalia();
		
		reavalia(html.replace("&","&amp;"));
	}
	/**
	 * Organiza o array armazenaErroOuAviso
	 * por ErrosRegraPro
	 */
	private void organizaErroOuAviso(ArrayList<ErrosRegraPro> lista,ArrayList<ArmazenaErroOuAviso> armazenaErroOuAviso){
		//Coloca no hashmap de forma ordenada
		HashMap<String,ArrayList<ArmazenaErroOuAviso>> mapa = new HashMap<String,ArrayList<ArmazenaErroOuAviso>>();
		for(int i=0;i<armazenaErroOuAviso.size();i++){
			if(!mapa.containsKey(armazenaErroOuAviso.get(i).getIdTextoRegra())){
				mapa.put(armazenaErroOuAviso.get(i).getIdTextoRegra(), new ArrayList<ArmazenaErroOuAviso>());				
			}
			armazenaErroOuAviso.get(i).setLinha(armazenaErroOuAviso.get(i).getLinha()+correcaoLinha);
			mapa.get(armazenaErroOuAviso.get(i).getIdTextoRegra())
			.add(armazenaErroOuAviso.get(i));
		}
		Map<String, ArrayList<ArmazenaErroOuAviso>> sortedMap = new TreeMap<String, ArrayList<ArmazenaErroOuAviso>>(mapa);
		List<String> mapKeys = new ArrayList<String>(sortedMap.keySet());
		ArrayList<ArrayList<ArmazenaErroOuAviso>> mapValues = new ArrayList<ArrayList<ArmazenaErroOuAviso>>(sortedMap.values());
		for(int i=0;i<mapKeys.size();i++){
			ErrosRegraPro errosRegra = new ErrosRegraPro();
			errosRegra.setCodigo(mapKeys.get(i));
			errosRegra.setTexto(regras.getTextoRegra(mapKeys.get(i)));
			errosRegra.setListOcorrencia(converte(mapValues.get(i)));
			lista.add(errosRegra);			
		}
	}
	
	private List<ErroOuAvisoPro> converte(List<ArmazenaErroOuAviso> listArmazenaErroOuAviso) {
		ArrayList<ErroOuAvisoPro> retorno = new ArrayList<ErroOuAvisoPro>();
		for(ArmazenaErroOuAviso armazenaErroOuAviso:listArmazenaErroOuAviso){
			retorno.add(converte(armazenaErroOuAviso));
		}
		return retorno;
	}

	private ErroOuAvisoPro converte(ArmazenaErroOuAviso erroOuAviso){
		ErroOuAvisoPro retorno = new ErroOuAvisoPro();
		if(erroOuAviso.isAviso()){
			retorno.setAviso();
		}else{
			retorno.setErro();
		}
		retorno.setCodTextoRegra(erroOuAviso.getIdTextoRegra());
		retorno.setColuna(erroOuAviso.getColuna());
		retorno.setLinha(erroOuAviso.getLinha());
		retorno.setTagCompleta(erroOuAviso.getTagCompleta());
		return retorno;
	}
	
	public void avisosP1(ArrayList<ArmazenaErroOuAviso> armazenaErroOuAviso) {
		avisoP1 = armazenaErroOuAviso;
		organizaErroOuAviso(listAvisoP1, armazenaErroOuAviso);		
	}

	private void reavalia(String codigo) {
		try {
			XHTMLControle xhtmlValidator = new XHTMLControle();
			try {
				xhtmlValidator.avalia(codigo);
			} catch (RuntimeException e) {
				e.printStackTrace();
			} catch(Exception e){
				e.printStackTrace();
			}
			for (int i = 0; i < xhtmlValidator.length(); i++) {
				ErrosRegraPro errosRegra1=new ErrosRegraPro();
				
				errosRegra1.setTexto(xhtmlValidator.getMensagem(i));
			
				List<ArmazenaErroOuAviso> armazenaErrosOuAvisos=new ArrayList<ArmazenaErroOuAviso>();
				ArmazenaErroOuAviso armazenaErroOuAviso=new ArmazenaErroOuAviso();
				armazenaErroOuAviso.setLinha(xhtmlValidator.getLinha(i));
				armazenaErroOuAviso.setColuna(xhtmlValidator.getColuna(i));
				avisoXHTML.add(armazenaErroOuAviso);
				erroXHTML.add(armazenaErroOuAviso);				
				armazenaErrosOuAvisos.add(armazenaErroOuAviso);
				errosRegra1.setListOcorrencia(converte(armazenaErrosOuAvisos));
			
				listErroXHTML.add(errosRegra1);
				//retirado por bug em DTD
				errado=true;
			}
			getRelatorio();
			// domValidator
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void avisosP2(ArrayList<ArmazenaErroOuAviso> armazenaErroOuAviso) {
		avisoP2 = armazenaErroOuAviso;
		organizaErroOuAviso(listAvisoP2, armazenaErroOuAviso);
	}

	
	public void avisosP3(ArrayList<ArmazenaErroOuAviso> armazenaErroOuAviso) {
		avisoP3 = armazenaErroOuAviso;
		organizaErroOuAviso(listAvisoP3, armazenaErroOuAviso);		
	}

	
	public void errosP1(ArrayList<ArmazenaErroOuAviso> armazenaErroOuAviso) {
		if(armazenaErroOuAviso!=null && armazenaErroOuAviso.size()>0){
			errado=true;
		}
		erroP1=armazenaErroOuAviso;
		organizaErroOuAviso(listErroP1, armazenaErroOuAviso);
		
	}

	
	public void errosP2(ArrayList<ArmazenaErroOuAviso> armazenaErroOuAviso) {
		if(armazenaErroOuAviso!=null && armazenaErroOuAviso.size()>0){
			errado=true;
		}
		erroP2=armazenaErroOuAviso;
		organizaErroOuAviso(listErroP2, armazenaErroOuAviso);
	}

	
	public void errosP3(ArrayList<ArmazenaErroOuAviso> armazenaErroOuAviso) {
		if(armazenaErroOuAviso!=null && armazenaErroOuAviso.size()>0){
			errado=true;
		}
		erroP3=armazenaErroOuAviso;
		organizaErroOuAviso(listErroP3, armazenaErroOuAviso);
		
	}

	
	public void fimDaAvaliacao() {
		
	}

	public RelatorioAcessibilidadePro getRelatorio(){
		//gerar o relatorio
		if(relatorio!=null) return relatorio;
		relatorio = new RelatorioAcessibilidadePro();
		relatorio.setListAvisoP1(listAvisoP1);
		relatorio.setListAvisoP2(listAvisoP2);
		relatorio.setListAvisoP3(listAvisoP3);
	
		relatorio.setListErroP1(listErroP1);
		relatorio.setListErroP2(listErroP2);
		relatorio.setListErroP3(listErroP3);
		relatorio.setListErroXHTML(listErroXHTML);
		
		//imprimeListaErr(listErroP1);
		//imprimeListaErr(listErroP2);
		//imprimeListaErr(listErroP3);
		return relatorio;
	}
	
	public void linksAchadosPeloNucleo(HashSet<String> arg0) {
		// TODO Auto-generated method stub
		
	}
	public boolean isErrado() {
		return errado;
	}
	public List<ArmazenaErroOuAviso> getListErroP1() {
		return erroP1;
	}
	public List<ArmazenaErroOuAviso> getListErroP2() {
		return erroP2;
	}
	public List<ArmazenaErroOuAviso> getListErroP3() {
		return erroP3;
	}
	public RegrasHardCodedEmag getRegras() {
		return regras;
	}
	public List<ArmazenaErroOuAviso> getAvisoP1() {
		return avisoP1;
	}
	public List<ArmazenaErroOuAviso> getAvisoP2() {
		return avisoP2;
	}
	public List<ArmazenaErroOuAviso> getAvisoP3() {
		return avisoP3;
	}
	public void setCorrecaoLinha(int i) {
		correcaoLinha = i;
	}
	
	

	
	public List<ArmazenaErroOuAviso> getErroXHTML() {
		return erroXHTML;
	}
	
}
